//#include "stdafx.h"
#include "Engine.h"
#include "Common/CommonString.h"
#include "Common/CommonVector.h"
#include "Common/CommonFile.h"
#include "Common/Output.h"
#include "Maths/Math.h"

#include "GL/glfw.h"

using namespace std;

//////////////////////////////////////////////////////////////////////////
Engine::Engine(void)
{

}


//////////////////////////////////////////////////////////////////////////
Engine::~Engine(void)
{
// 	glDeleteVertexArrays(1, &mVertexArrayID);

	//
	delete mScene;
}

//////////////////////////////////////////////////////////////////////////
void Engine::Init()
{
	// Dark gray background
	glClearColor(0.4f, 0.4f, 0.4f, 0.0f);

	//glCullFace(GL_BACK);
	//glEnable(GL_CULL_FACE);
	glEnable(GL_DEPTH_TEST);
	glDepthFunc(GL_LESS);

	// Create and compile our GLSL program from the shaders
	mProgramID = LoadShaders( "shaders/SimpleVertexShader.vs", "shaders/SimpleFragmentShader.fs" );

	mUniform1 = GetUniform(mProgramID, "mUniform1");

	mScene = new Scene();
	mScene->Init(mProgramID);

	//glEnable(GL_CULL_FACE);
	mTransposeMatrix = true;
}

//////////////////////////////////////////////////////////////////////////
void Engine::Update(float aElapsedTime)
{
	// OpenGL rendering goes here...
	glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

	// Use our shader
	glUseProgram(mProgramID);
	glUniform1f(mUniform1, 1.0f/*Math::sinf(aElapsedTime)*/);
	
	mScene->Update(aElapsedTime, mTransposeMatrix);

	// Swap front and back rendering buffers
	glfwSwapBuffers();
}

//////////////////////////////////////////////////////////////////////////
void Engine::SetViewPortMat(const Matrix* aProjMat)
{
	mScene->SetProjectionMatrix(aProjMat);
}

//////////////////////////////////////////////////////////////////////////
GLuint Engine::LoadShaders(const char * vertex_file_path, const char * fragment_file_path)
{
 	// Create the shaders
  		GLuint tVertexShaderID = glCreateShader(GL_VERTEX_SHADER);
  		GLuint tFragmentShaderID = glCreateShader(GL_FRAGMENT_SHADER);
  	
  		// Read the Vertex Shader code from the file
  		CommonString VertexShaderCode;
		{
			CommonFile VertexShaderFile(vertex_file_path);
			if (VertexShaderFile.IsOpen())
			{
				CommonString Line = "";
				while(VertexShaderFile.GetLine(Line))
				{
					VertexShaderCode += "\n";
					VertexShaderCode += Line;
				}
			}
			else
			{
				Output::Print(vertex_file_path);
				Output::Printf("\n!! Warning cannot open %s\n", vertex_file_path);
			}
		}

  		// Read the Fragment Shader code from the file
		CommonString FragmentShaderCode;
		{
			CommonFile FragmentShaderStream(fragment_file_path);
			if(FragmentShaderStream.IsOpen())
			{
				CommonString Line = "";
				while(FragmentShaderStream.GetLine(Line))
				{
					FragmentShaderCode += "\n" ;
					FragmentShaderCode += Line;
				}
			}
			else
			{
				Output::Print(fragment_file_path);
				Output::Printf("\n!! Warning cannot open %s\n", fragment_file_path);
			}
		}
  	
  		// Compile Vertex Shader
  		std::printf("Compiling shader : %s\n", vertex_file_path);
  		char const * VertexSourcePointer = VertexShaderCode.GetStdString().c_str();
  		glShaderSource(tVertexShaderID, 1, &VertexSourcePointer , NULL);
  		glCompileShader(tVertexShaderID);

		GLint Result = GL_FALSE;
  		int InfoLogLength;
  		// Check Vertex Shader
  		glGetShaderiv(tVertexShaderID, GL_COMPILE_STATUS, &Result);
  		glGetShaderiv(tVertexShaderID, GL_INFO_LOG_LENGTH, &InfoLogLength);

  		if (InfoLogLength > 0)
		{
			CommonVector<char> VertexShaderErrorMessage(InfoLogLength);
			glGetShaderInfoLog(tVertexShaderID, InfoLogLength, NULL, &VertexShaderErrorMessage[0]);
			Output::Print(CommonString(VertexShaderErrorMessage));
			//Output::Printf("%s\n", &VertexShaderErrorMessage[0]);
  		}
  	
  		// Compile Fragment Shader
  		std::printf("Compiling shader : %s\n", fragment_file_path);
  		char const * FragmentSourcePointer = FragmentShaderCode.GetStdString().c_str();
  		glShaderSource(tFragmentShaderID, 1, &FragmentSourcePointer , NULL);
  		glCompileShader(tFragmentShaderID);
  	
  		// Check Fragment Shader
  		glGetShaderiv(tFragmentShaderID, GL_COMPILE_STATUS, &Result);
  		glGetShaderiv(tFragmentShaderID, GL_INFO_LOG_LENGTH, &InfoLogLength);
		if (InfoLogLength > 0)
		{
			CommonVector<char> FragmentShaderErrorMessage(InfoLogLength);
			glGetShaderInfoLog(tFragmentShaderID, InfoLogLength, NULL, &FragmentShaderErrorMessage[0]);
			Output::Print(CommonString(FragmentShaderErrorMessage));
			//Output::Printf("%s\n", &FragmentShaderErrorMessage[0]);
		}
  	
  		// Link the program
  		Output::Printf("Linking program\n");
  		GLuint ProgramID = glCreateProgram();
  		glAttachShader(ProgramID, tVertexShaderID);
  		glAttachShader(ProgramID, tFragmentShaderID);
  		glLinkProgram(ProgramID);
  	
		// Check the program
		glGetProgramiv(ProgramID, GL_LINK_STATUS, &Result);
		glGetProgramiv(ProgramID, GL_INFO_LOG_LENGTH, &InfoLogLength);
  		CommonVector<char> ProgramErrorMessage( max(InfoLogLength, int(1)) );
  		glGetProgramInfoLog(ProgramID, InfoLogLength, NULL, &ProgramErrorMessage[0]);
  		Output::Print(CommonString(ProgramErrorMessage));

		glDeleteShader(tVertexShaderID);
		glDeleteShader(tFragmentShaderID);
  	
 	 	return ProgramID;
}